package org.xteam.box2text.oaw;

import java.io.Reader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.mwe.core.monitor.ProgressMonitor;
import org.eclipse.internal.xtend.expression.parser.SyntaxConstants;
import org.eclipse.internal.xtend.util.Cache;
import org.eclipse.internal.xtend.util.Pair;
import org.eclipse.internal.xtend.util.Triplet;
import org.eclipse.internal.xtend.xtend.ast.Around;
import org.eclipse.internal.xtend.xtend.ast.Extension;
import org.eclipse.xtend.expression.Callback;
import org.eclipse.xtend.expression.ExceptionHandler;
import org.eclipse.xtend.expression.ExecutionContextImpl;
import org.eclipse.xtend.expression.NullEvaluationHandler;
import org.eclipse.xtend.expression.Resource;
import org.eclipse.xtend.expression.ResourceManager;
import org.eclipse.xtend.expression.ResourceParser;
import org.eclipse.xtend.expression.TypeSystemImpl;
import org.eclipse.xtend.expression.Variable;
import org.eclipse.xtend.typesystem.Type;

public class BoxExecutionContext extends ExecutionContextImpl {

	public BoxExecutionContext() {
		super();
		registerParser(resourceManager);
	}

	public BoxExecutionContext(
			ResourceManager resourceManager,
			Resource currentResource,
			TypeSystemImpl typeSystem,
			Map<String, Variable> visibleVariables,
			Map<String, Variable> globalVariables,
			ProgressMonitor monitor,
			ExceptionHandler exceptionHandler,
			List<Around> registeredExtensionAdvices,
			NullEvaluationHandler nullEvaluationHandler,
			Map<Resource, Set<Extension>> allExtensionsPerResource,
			Callback callback,
			Cache<Triplet<Resource, String, List<Type>>, Extension> extensionsForNameAndTypesCache,
			Map<Pair<String, List<Type>>, Type> extensionsReturnTypeCache) {
		super(resourceManager, currentResource, typeSystem, visibleVariables,
				globalVariables, monitor, exceptionHandler,
				registeredExtensionAdvices, nullEvaluationHandler,
				allExtensionsPerResource, callback,
				extensionsForNameAndTypesCache, extensionsReturnTypeCache);
	}

	private void registerParser(ResourceManager resourceManager) {
		resourceManager.registerParser(BoxUtil.BOX_EXTENSION,
				new ResourceParser() {
					public Resource parse(Reader in, String fileName) {
						return BoxParseFacade.file(in, fileName);
					}
				});
	}

	@Override
	public ExecutionContextImpl cloneContext() {
		final BoxExecutionContext result = new BoxExecutionContext(
				resourceManager, currentResource(), typeSystem,
				getVisibleVariables(), getGlobalVariables(), getMonitor(),
				exceptionHandler, registeredExtensionAdvices,
				nullEvaluationHandler, allExtensionsPerResource, callback,
				this.extensionsForNameAndTypesCache,
				this.extensionsReturnTypeCache);
		return result;
	}

	public BoxRule findRule(String name, Type target) {
		BoxResource tpl = null;
		if (name.length() > 0) { // local call
			tpl = findTemplate(name);
		} else {
			tpl = (BoxResource) currentResource();
		}
		if (tpl == null)
			return null;
		final BoxExecutionContext ctx = (BoxExecutionContext) cloneWithResource(tpl);
		return findRule(tpl.getRules(), target, ctx);
	}

	public BoxResource findTemplate(final String templateName) {
		return findTemplate(templateName, getImportedNamespaces());
	}

	public BoxResource findTemplate(final String templateName,
			String[] importedNs) {
		final List<?> possibleNames = typeSystem.getPossibleNames(templateName,
				importedNs);
		for (final Iterator<?> iter = possibleNames.iterator(); iter.hasNext();) {
			final String element = (String) iter.next();
			final BoxResource tpl = (BoxResource) resourceManager.loadResource(
					element, BoxUtil.BOX_EXTENSION);
			if (tpl != null)
				return tpl;
		}
		return null;
	}

	private BoxRule findRule(final BoxRule[] rules, final Type target, final BoxExecutionContext ctx) {
		for (BoxRule rule : rules) {
			Type t = ctx.getTypeForName(rule.getTargetType());
			if (t == target) {
				return rule;
			}
		}
		return null;
	}

}
